package com.yunyi.camunda.service.impl;

import com.yunyi.camunda.common.constant.OperateConstants;
import com.yunyi.camunda.common.exception.YunyiException;
import com.yunyi.camunda.common.utils.UserUtils;
import com.yunyi.camunda.mapper.ITaskRecordMapper;
import com.yunyi.camunda.pojo.Result;
import com.yunyi.camunda.pojo.dto.TaskRecord;
import com.yunyi.camunda.pojo.dto.User;
import com.yunyi.camunda.pojo.vo.CompleteTaskRequest;
import com.yunyi.camunda.pojo.vo.RejectTaskRequest;
import com.yunyi.camunda.pojo.vo.TransferTaskRequest;
import com.yunyi.camunda.service.ProcessTaskService;
import org.apache.commons.lang3.StringUtils;
import org.camunda.bpm.engine.HistoryService;
import org.camunda.bpm.engine.RuntimeService;
import org.camunda.bpm.engine.TaskService;
import org.camunda.bpm.engine.history.HistoricActivityInstance;
import org.camunda.bpm.engine.runtime.ActivityInstance;
import org.camunda.bpm.engine.task.Comment;
import org.camunda.bpm.engine.task.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * task操作类
 *
 * @author yunyi
 */
@Service
public class ProcessTaskServiceImpl implements ProcessTaskService {

    @Autowired
    private ITaskRecordMapper taskRecordMapper;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private HistoryService historyService;

    @Autowired
    private TaskService taskService;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Result<Map<String, String>> completeSingleTask(CompleteTaskRequest requestParam) {
        Task task = this.checkTask(requestParam.getProcessInstanceId(), requestParam.getTaskId());
        //完成之前还需要先交验一下登陆用户是否跟任务操作人一致，这里就先忽略了
        taskService.complete(task.getId());
        //自定义的记录表，将每一个操作和评论等信息进行记录，查找相关记录也方便
        //模拟获取当前登陆用户的信息
        User virtualUser = UserUtils.getVirtualUser();
        //自定义流水表
        this.saveRecord(OperateConstants.SUCCESS, requestParam.getComments(), task.getProcessInstanceId(),
                task.getTaskDefinitionKey(), String.valueOf(virtualUser.getId()));
        //camunda保存评论的接口
        if (StringUtils.isNoneBlank(requestParam.getComments())) {
            this.addComment(task.getProcessInstanceId(), task.getId(), requestParam.getComments());
        }
        return Result.successMessage("审批成功");
    }


    @Override
    public void addComment(String processInstanceId, String taskId, String comment) {
        Comment comment1 = taskService.createComment(taskId, processInstanceId, comment);
        System.out.println(comment1);
    }


    @Override
    public Result<Void> transferTask(TransferTaskRequest requestParam) {
        Task task = this.checkTask(requestParam.getProcessInstanceId(), requestParam.getTaskId());
        taskService.setAssignee(requestParam.getTaskId(), requestParam.getUserId());
        User virtualUser = UserUtils.getVirtualUser();
        this.saveRecord(OperateConstants.REDIRECT, requestParam.getComments(), task.getProcessInstanceId(),
                task.getTaskDefinitionKey(), String.valueOf(virtualUser.getId()));
        if (StringUtils.isNoneBlank(requestParam.getComments())) {
            this.addComment(task.getProcessInstanceId(), task.getId(), requestParam.getComments());
        }
        return Result.successMessage("转办成功");
    }

    @Override
    public Result<Void> rejectTask(RejectTaskRequest requestParam) {
        String processInstanceId = requestParam.getProcessInstanceId();
        Task task = taskService.createTaskQuery()
                .processInstanceId(processInstanceId)
                .singleResult();
        ActivityInstance tree = runtimeService.getActivityInstance(processInstanceId);
        //获取所有已办用户任务节点
        List<HistoricActivityInstance> resultList = historyService
                .createHistoricActivityInstanceQuery()
                .processInstanceId(processInstanceId)
                .activityType("userTask")
                .finished()
                .orderByHistoricActivityInstanceEndTime()
                .asc()
                .list();
        if (null == resultList || resultList.size() == 0) {
            return Result.failMessage("当前任务无法驳回！");
        }
        //得到第一个任务节点的id
        HistoricActivityInstance historicActivityInstance = resultList.get(0);
        String startActId = historicActivityInstance.getActivityId();
        if (startActId.equals(task.getTaskDefinitionKey())) {
            return Result.failMessage("开始节点无法驳回！");
        }
        //得到上一个任务节点的ActivityId和待办人
        Map<String, String> lastNode = this.getLastNode(resultList, task.getTaskDefinitionKey());
        if (null == lastNode) {
            return Result.failMessage("回退节点异常！");
        }
        String toActId = lastNode.get("toActId");
        String assignee = lastNode.get("assignee");
        //设置流程中的可变参数
        Map<String, Object> taskVariable = new HashMap<>(2);
        taskVariable.put("user", assignee);
        if (StringUtils.isNotBlank(requestParam.getComments())) {
            this.addComment(processInstanceId, task.getId(), requestParam.getComments());
        }
        //其实可以通过这个不只是回退到上个节点，也可以回退到之前的任意一个节点
        runtimeService.createProcessInstanceModification(processInstanceId)
                //关闭相关任务
                .cancelActivityInstance(this.getInstanceIdForActivity(tree, task.getTaskDefinitionKey()))
                .setAnnotation("进行了驳回到上一个任务节点操作")
                //启动目标活动节点
                .startBeforeActivity(toActId)
                //流程的可变参数赋值
                .setVariables(taskVariable)
                .execute();
        Task newTask = taskService.createTaskQuery()
                .processInstanceId(processInstanceId)
                .singleResult();
        taskService.setAssignee(newTask.getId(), assignee);
        User virtualUser = UserUtils.getVirtualUser();
        this.saveRecord(OperateConstants.REJECT, requestParam.getComments(), task.getProcessInstanceId(),
                task.getTaskDefinitionKey(), String.valueOf(virtualUser.getId()));
        return Result.success();
    }

    @Override
    public Result<String> claimTask(String taskId) {
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        User virtualUser = UserUtils.getVirtualUser();
        if (task == null) {
            return Result.failMessage("领取任务失败");
        }
        taskService.claim(taskId, String.valueOf(virtualUser.getId()));
        this.saveRecord(OperateConstants.CLAIM, null, task.getProcessInstanceId(),
                task.getTaskDefinitionKey(), String.valueOf(virtualUser.getId()));
        return Result.success();
    }

    /**
     * 检查任务是否存在
     *
     * @param processInstanceId 流程实例id
     * @param taskId            任务id
     * @return task
     */
    private Task checkTask(String processInstanceId, String taskId) {
        Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).taskId(taskId).singleResult();
        if (task == null) {
            throw new YunyiException("没有查询到任务");
        }
        return task;
    }


    /**
     * 保存流水
     * 如果需要保存业务id，那么需要从task拿到processInstance，再从中拿到busId
     * 这里保存了节点的defineKey作为nodeId，其实可以使用taskId,按照自己需求扩展
     *
     * @param operate           操作类型
     * @param comments          评论
     * @param processInstanceId 流程id
     * @param nodeId            节点id
     * @param userId            用户id
     */
    private void saveRecord(String operate, String comments,
                            String processInstanceId, String nodeId, String userId) {
        TaskRecord taskRecord = new TaskRecord();
        taskRecord.setAnnex(null);
        //这个字段没太大用处,跟附件字段一样仅仅是体现一下自行扩展
        taskRecord.setSysComments(operate);
        taskRecord.setOperate(operate);
        taskRecord.setComments(comments);
        taskRecord.setProcessInstanceId(processInstanceId);
        taskRecord.setNodeId(nodeId);
        taskRecord.setUserId(userId);
        taskRecordMapper.saveRecord(taskRecord);
    }


    private Map<String, String> getLastNode(List<HistoricActivityInstance> resultList, String currentActivityId) {
        Map<String, String> backNode = new HashMap<>();
        //新建一个有序不重复集合
        LinkedHashMap<String, String> linkedHashMap = new LinkedHashMap();
        for (HistoricActivityInstance hai : resultList) {
            linkedHashMap.put(hai.getActivityId(), hai.getAssignee());
        }
        //分两种情况：当前节点在不在历史节点里面，当前节点在历史节点里
        //情况1、当前节点不在历史节点里
        int originSize = resultList.size();
        int duplicateRemovalSize = linkedHashMap.size();
        //判断历史节点中是否有重复节点
        //if(originSize == duplicateRemovalSize){
        boolean flag = false;
        for (Map.Entry entry : linkedHashMap.entrySet()) {
            if (currentActivityId.equals(entry.getKey())) {
                flag = true;
                break;
            }
        }
//            if(flag){
//                //当前节点在历史节点里：最后一个节点是回退节点
//                return currentNodeInHis(linkedHashMap, currentActivityId);
//            }
        if (!flag) {
            //当前节点不在历史节点里：最后一个节点是完成节点
            HistoricActivityInstance historicActivityInstance = resultList.get(originSize - 1);
            backNode.put("toActId", historicActivityInstance.getActivityId());
            backNode.put("assignee", historicActivityInstance.getAssignee());
            return backNode;
        }
        //}
        //情况2、当前节点在历史节点里（已回退过的）
        return currentNodeInHis(linkedHashMap, currentActivityId);
    }

    private Map<String, String> currentNodeInHis(LinkedHashMap<String, String> linkedHashMap, String currentActivityId) {
        //情况2、当前节点在历史节点里（已回退过的）
        Map<String, String> backNode = new HashMap<>();
        ListIterator<Map.Entry<String, String>> li = new ArrayList<>(linkedHashMap.entrySet()).listIterator();
        //System.out.println("已回退过的");
        while (li.hasNext()) {
            Map.Entry<String, String> entry = li.next();
            if (currentActivityId.equals(entry.getKey())) {
                li.previous();
                Map.Entry<String, String> previousEntry = li.previous();
                backNode.put("toActId", previousEntry.getKey());
                backNode.put("assignee", previousEntry.getValue());
                return backNode;
            }
        }
        return null;
    }

    private String getInstanceIdForActivity(ActivityInstance activityInstance, String activityId) {
        ActivityInstance instance = getChildInstanceForActivity(activityInstance, activityId);
        if (instance != null) {
            return instance.getId();
        }
        return null;
    }

    private ActivityInstance getChildInstanceForActivity(ActivityInstance activityInstance, String activityId) {
        if (activityId.equals(activityInstance.getActivityId())) {
            return activityInstance;
        }
        for (ActivityInstance childInstance : activityInstance.getChildActivityInstances()) {
            ActivityInstance instance = getChildInstanceForActivity(childInstance, activityId);
            if (instance != null) {
                return instance;
            }
        }
        return null;
    }


}
